/*********************************************************
          File Name:unique_ptr.h
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Wed 16 Dec 2015 09:04:01 AM CST
**********************************************************/

#ifndef UNIQUE_PTR_H_
#define UNIQUE_PTR_H_

#include <functional>

namespace nm
{
        template<typename T>
        struct default_deleter
        {
                void operator()(T *d)
                {
                        if(d)
                        {
                                delete d;
                                d = nullptr;
                        }
                }
        };

        
        template<typename T, typename Deleter = nm::default_deleter<T>>
        class unique_ptr;

        template<typename T, typename Deleter = nm::default_deleter<T>>
        void swap(unique_ptr<T, Deleter> &lhs, unique_ptr<T, Deleter> &rhs)
        {
                using std::swap;
                swap(lhs.ptr, rhs.ptr);
                swap(lhs.deleter, rhs.deleter);
        }
        
        template<typename T, typename Deleter>
        class unique_ptr final
        {
                private:
                        T *ptr;
                        Deleter deleter;

                public:
                        unique_ptr()
                        {
                                ptr = nullptr;
                                deleter = nm::default_deleter<T>();
                        }
                        unique_ptr(std::nullptr_t) : unique_ptr() {}
                        unique_ptr(const unique_ptr &) = delete; 
                        unique_ptr(unique_ptr &&uq)
                        {
                                ptr = uq.ptr;
                                deleter = uq.deleter;
                                uq.ptr = nullptr;
                        }
                        explicit unique_ptr(T *p)
                        {
                                ptr = p;
                                deleter = nm::default_deleter<T>();
                        }
                        unique_ptr(T *p, Deleter d)
                        {
                                if(p != nullptr)
                                {
                                        ptr = p;
                                        deleter = d;
                                }
                        }
                        unique_ptr &operator=(const unique_ptr &) = delete;
                        unique_ptr &operator=(std::nullptr_t) noexcept
                        {
                                deleter(ptr);
                                ptr = nullptr;
                                return *this;
                        }
                        unique_ptr &operator=(unique_ptr &&uq)
                        {
                                deleter(ptr);
                                ptr = std::move(uq.ptr);
                                deleter = uq.deleter;

                                uq.ptr = nullptr;
                                return *this;
                        }

                        ~unique_ptr()
                        {
                                deleter(ptr);
                        }

                        T *release()
                        {
                                T *res = ptr;
                                ptr = nullptr;
                                return res;
                        }

                        void reset(T *p)
                        {
                                auto old = ptr;
                                ptr = p;
                                if(old)
                                        deleter(old);
                        }

                        void swap(unique_ptr &rhs)
                        {
                                nm::swap(*this, rhs);
                        }

                        T *get() const
                        {
                                return ptr;
                        }

                        Deleter &get_deleter()
                        {
                                return deleter;
                        }

                        const Deleter &get_deleter() const
                        {
                                return deleter;
                        }

                        explicit operator bool() const
                        {
                                return ptr != nullptr;
                        }
                        
                        T &operator*() const
                        {
                                return *ptr;
                        }

                        T *operator->() const
                        {
                                return ptr;
                        }
        };
}

#endif
